package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"

	"github.com/hyperledger/fabric-contract-api-go/contractapi"
	"github.com/xlcetc/cryptogm/sm/sm3"
)

// SmartContract provides functions for managing an Asset
type SmartContract struct {
	contractapi.Contract
}

type BasicResponse struct {
	Code string      `json:"code"`
	Msg  string      `json:"msg"`
	Data interface{} `json:"data"`
}

const (
	RECODE_OK                  = "0"
	RECODE_UNKNOWERR           = "999"
	RECODE_USER_ALREADY_EXISTS = "100"
	RECODE_USER_NOT_EXISTS     = "101"
	RECODE_PASSWORD_ERR        = "102"
	RECODE_BALANCE_NOT_ENOUGH  = "103"
	RECODE_TASK_NOT_EXISTS     = "104"
	RECODE_TASK_CANNOT_TAKE    = "105"
	RECODE_TASK_CANNOT_COMMIT  = "106"
	RECODE_TASK_CANNOT_CONFRIM = "107"
	RECODE_CHAINCODE_PUT_ERR   = "108"
	RECODE_CHAINCODE_GET_ERR   = "109"
	RECODE_CHAINCODE_DEL_ERR   = "110"
	RECODE_JSON_ERR            = "111"
)

type UserInfo struct {
	UserName string `json:"user_name"`
	PassWord string `json:"pass_word"`
	NickName string `json:"nick_name"`
}

func (s *SmartContract) Register(
	ctx contractapi.TransactionContextInterface,
	name, pass, nickname string) *BasicResponse {

	resp := BasicResponse{RECODE_OK, "OK", []byte{}}

	userJson, err := ctx.GetStub().GetState(name)
	if err != nil {
		resp.Code = RECODE_CHAINCODE_GET_ERR
		resp.Msg = err.Error()
		return &resp
	}
	if userJson != nil {
		resp.Code = RECODE_USER_ALREADY_EXISTS
		resp.Msg = fmt.Errorf("user already exists").Error()
		return &resp
	}

	user := UserInfo{name, pass, nickname}
	userJson, err = json.Marshal(user)
	if err != nil {
		resp.Code = RECODE_JSON_ERR
		resp.Msg = err.Error()
		return &resp
	}

	err = ctx.GetStub().PutState(user.UserName, userJson)
	if err != nil {
		resp.Code = RECODE_CHAINCODE_PUT_ERR
		resp.Msg = fmt.Errorf("failed to put to world state. %v", err).Error()
		return &resp
	}

	return &resp

}

func (s *SmartContract) Login(
	ctx contractapi.TransactionContextInterface,
	id, pass string) *BasicResponse {

	resp := BasicResponse{RECODE_OK, "OK", []byte{}}

	userJson, err := ctx.GetStub().GetState(id)
	if err != nil {
		resp.Code = RECODE_CHAINCODE_GET_ERR
		resp.Msg = fmt.Errorf("failed to read from world state: %v", err).Error()
		return &resp
	}
	if userJson == nil {
		resp.Code = RECODE_USER_NOT_EXISTS
		return &resp
	}

	var userinfo UserInfo
	err = json.Unmarshal(userJson, &userinfo)
	if err != nil {
		resp.Code = RECODE_JSON_ERR
		return &resp
	}

	if userinfo.PassWord != pass {
		resp.Code = RECODE_PASSWORD_ERR
		return &resp
	}

	resp.Data = userinfo.NickName

	return &resp

}

func (s *SmartContract) ChangePass(
	ctx contractapi.TransactionContextInterface,
	id, oldpass, newpass string) *BasicResponse {

	resp := BasicResponse{RECODE_OK, "OK", []byte{}}
	userJson, err := ctx.GetStub().GetState(id)
	if err != nil {
		resp.Code = RECODE_CHAINCODE_GET_ERR
		resp.Msg = err.Error()
		return &resp
	}
	if userJson == nil {
		resp.Code = RECODE_USER_NOT_EXISTS
		return &resp
	}

	var userinfo UserInfo
	err = json.Unmarshal(userJson, &userinfo)
	if err != nil {
		resp.Code = RECODE_JSON_ERR
		return &resp
	}

	if userinfo.PassWord != oldpass {
		resp.Code = RECODE_PASSWORD_ERR
		resp.Msg = fmt.Errorf("user's password err").Error()
		return &resp
	}
	userinfo.PassWord = newpass

	userJson, err = json.Marshal(&userinfo)
	if err != nil {
		resp.Code = RECODE_JSON_ERR
		resp.Msg = err.Error()
		return &resp
	}

	err = ctx.GetStub().PutState(userinfo.UserName, userJson)
	if err != nil {
		resp.Code = RECODE_CHAINCODE_PUT_ERR
		resp.Msg = err.Error()
		return &resp
	}

	return &resp

}

func makeSm3Token(msg string, nowtime string) string {

	// 使用区块链的时间戳

	data := bytes.Join([][]byte{[]byte(msg),
		[]byte(nowtime),
	}, []byte{})
	return fmt.Sprintf("%x", sm3.SumSM3(data))
}

func main() {
	chaincode, err := contractapi.NewChaincode(&SmartContract{})
	if err != nil {
		log.Panicf("Error creating user chaincode: %v", err)
	}

	if err := chaincode.Start(); err != nil {
		log.Panicf("Error starting user chaincode: %v", err)
	}
}
